#include "bga/graphics/MD2Loader.h"
#include <fstream>
#include <iostream>
#include <cstring>

// id "IDP2" or 844121161
#define MD2_IDENT (('2'<<24) + ('P'<<16) + ('D'<<8) + 'I')

// version number
#define MD2_VERSION 8

using namespace std;


namespace bga
{

MD2Loader::MD2Vector MD2Loader::normal[] = {
            #include "bga/graphics/Anorms.h"
            };

MD2Loader::MD2Loader() : GraphicLoader(), skins(NULL),textureCoordinates(NULL), triangles(NULL), frames(NULL)
{

}

MD2Loader::~MD2Loader()
{
    if(this->skins != NULL)
    {
        delete[] this->skins;
        this->skins = NULL;
    }

    if(this->textureCoordinates != NULL)
    {
        delete[] this->textureCoordinates;
        this->textureCoordinates = NULL;
    }

    if(this->triangles != NULL)
    {
        delete[] this->triangles;
        this->triangles = NULL;
    }

    if(this->frames != NULL)
    {
        for(int i = 0; i < this->header.numFrames; i++)
        {
            if(this->frames[i].vertex != NULL)
            {
                delete[] this->frames[i].vertex;
                this->frames[i].vertex = NULL;
            }
        }
        delete[] this->frames;
        this->frames = NULL;
    }

}

void MD2Loader::load(const string& filename)
{

    Drawable drawable;

    std::ifstream file;
    file.open(filename.c_str(), std::ios::in | std::ios::binary);
    if(file.is_open())
    {
        file.read(reinterpret_cast<char*>(&header), sizeof(MD2Header));


        cout << "ident : " << header.ident << endl;
        cout << "version : " << header.version << endl;
        cout << "skinwidth : " << header.skinWidth << endl;
        cout << "skinheight : " << header.skinHeight << endl;
        cout << "frame size : " << header.framesize << endl;
        cout << "num skins : " << header.numSkins << endl;
        cout << "num vertex : " << header.numVertex << endl;
        cout << "num textCoord : " << header.numTextureCoordinates << endl;
        cout << "num triangle : " << header.numTriangles << endl;
        cout << "num gl commands : " << header.nuglcmds << endl;
        cout << "num frame : " << header.numFrames << endl;
        cout << "offset skins : " << header.offsetSkins << endl;
        cout << "offset textCoord : " << header.offsetTexture << endl;
        cout << "offset triangle : " << header.offsetTriangles << endl;
        cout << "offset frames : " << header.offsetFrames << endl;
        cout << "offset gl command : " << header.offset_glcmds << endl;
        cout << "offset end : " << header.offset_end << endl;


        this->skins = new MD2Skin[header.numSkins];
        this->textureCoordinates = new MD2Texture[header.numTextureCoordinates];
        this->triangles = new MD2Triangle[header.numTriangles];
        this->frames = new MD2Frame[header.numFrames];

        file.seekg(header.offsetSkins, std::ios::beg);
        file.read(reinterpret_cast<char*>(this->skins), sizeof(char) * 68 * this->header.numSkins);

        for(int i = 0; i < header.numSkins; i++)
        {
            cout << "Texture " << i << " : " << this->skins[i].skinFilename << endl;
        }

        file.seekg(header.offsetTexture, std::ios::beg);
        file.read(reinterpret_cast<char*>(this->textureCoordinates), sizeof(MD2Texture) * this->header.numTextureCoordinates);

        file.seekg(header.offsetTriangles, std::ios::beg);
        file.read(reinterpret_cast<char*>(this->triangles), sizeof(MD2Triangle) * this->header.numTriangles);

        file.seekg(header.offsetFrames, std::ios::beg);

        for(int i = 0; i < this->header.numFrames; i++)
        {
            this->frames[i].vertex = new MD2Vertex[this->header.numVertex];
            file.read(reinterpret_cast<char*>(this->frames[i].scale), sizeof(float) * 3 );
            file.read(reinterpret_cast<char*>(this->frames[i].translation), sizeof(float) * 3 );
            file.read(reinterpret_cast<char*>(this->frames[i].name), sizeof(char) * 16 );
            file.read(reinterpret_cast<char*>(this->frames[i].vertex), sizeof(MD2Vertex) * this->header.numVertex );
        }

        file.close();
    }

}

void MD2Loader::getDrawable(Drawable& drawable)
{
    /// frame is NULL
    if(this->frames != NULL)
    {
        float vertexArray[this->header.numVertex * 3];
        float normalArray[this->header.numVertex * 3];

        for(int i = 0; i < this->header.numVertex; i++)
        {
            vertexArray[i * 3] = this->frames[0].vertex[i].v[0];
            vertexArray[i * 3 + 1] = this->frames[0].vertex[i].v[1];
            vertexArray[i * 3 + 2] = this->frames[0].vertex[i].v[2];

            MD2Vector vect = MD2Loader::normal[this->frames[0].vertex[i].normalIndex];
            normalArray[i * 3] = vect.vertex[0];
            normalArray[i * 3 + 1] = vect.vertex[1];
            normalArray[i * 3 + 2] = vect.vertex[2];


            ///memcpy(reinterpret_cast<void*>(tabIndex[i*3]), this->triangles[i].indexVertex, 3);
        }

        /**cout << "normale" << endl;

        /// Weird but work ;)
        for(unsigned int i = 0; i < this->header.numVertex; i++)
        {
            cout << normalArray[i * 3] << ";" << normalArray[i * 3 + 1] << ";" << normalArray[i * 3 + 2] << endl;
        }

        cout << " fin normale" << endl;*/


        /// Load the texture trying to align index on the vertex ones
        float texCoordArray[this->header.numTextureCoordinates * 2];
        for(int i = 0; i < this->header.numTextureCoordinates; i++)
        {
            texCoordArray[i * 2] = static_cast<float>(this->textureCoordinates[i].s) / static_cast<float>(header.skinWidth);
            texCoordArray[i * 2 + 1] = static_cast<float>(this->textureCoordinates[i].t) / static_cast<float>(header.skinHeight);
        }

        for(int i = 0; i < this->header.numTextureCoordinates; i++)
        {
            cout << "s : " << texCoordArray[i * 2] << endl;
            cout << "t : " << texCoordArray[i * 2 + 1] << endl << endl;
        }

        unsigned short indexArray[this->header.numTriangles * 3];
        unsigned char indexArrayCoord[this->header.numTriangles * 3];


        for(int i = 0; i < this->header.numTriangles; i++)
        {
            indexArray[i * 3] = this->triangles[i].indexVertex[0];
            indexArray[i * 3 + 1] = this->triangles[i].indexVertex[1];
            indexArray[i * 3 + 2] = this->triangles[i].indexVertex[2];

            indexArrayCoord[i * 3] = this->triangles[i].indexTexCoord[0];
            indexArrayCoord[i * 3 + 1] = this->triangles[i].indexTexCoord[1];
            indexArrayCoord[i * 3 + 2] = this->triangles[i].indexTexCoord[2];

            /*this->swap(texCoordArray, 2, indexArray[i * 3], texCoordArray[i * 3]);
            this->swap(texCoordArray, 2, indexArray[i * 3 + 1], texCoordArray[i * 3 + 1]);
            this->swap(texCoordArray, 2, indexArray[i * 3 + 2], texCoordArray[i * 3 + 2]);*/


            ///memcpy(reinterpret_cast<void*>(tabIndex[i*3]), this->triangles[i].indexVertex, 3);
        }

        drawable.setPrimitive(GL_TRIANGLE_STRIP);
        for(int i = 0; i < this->header.numTriangles; i++)
        {
            cout << "vertex triangle " << i << " : " << (float)(indexArray[i * 3]) << ";" << (float)(indexArray[i * 3 + 1]) << ";" << (float)(indexArray[i * 3 + 2]) << endl;
            cout << "texture triangle " << i << " : " << (float)(indexArrayCoord[i * 3]) << ";" << (float)(indexArrayCoord[i * 3 + 1]) << ";" << (float)(indexArrayCoord[i * 3 + 2]) << endl;
        }

        drawable.setPrimitive(GL_TRIANGLE_STRIP);
        drawable.setVertexArray(vertexArray, this->header.numVertex * 3);
        drawable.setTexCoordArray(texCoordArray, this->header.numTextureCoordinates * 2);
        drawable.setNormalsArray(normalArray, this->header.numVertex * 3);
        drawable.setIndexArray(indexArray, this->header.numTriangles * 3);
    }
}

void MD2Loader::getTexture(Texture2D& texture)
{
    string skinName("./models/");
    skinName += skins[0].skinFilename;

    /// We should test if filename ends with a known extension. For the the moment, we will assume it;
    skinName += ".pcx";

    texture.bind();
    texture.setWrapS(GL_REPEAT);
    texture.setWrapT(GL_REPEAT);
    texture.setMinFilter(GL_LINEAR);
    texture.setMagFilter(GL_LINEAR);
    texture.setImage(skinName);
    texture.unbind();
}

void MD2Loader::setSceneNode(SceneNode& sceneNode)
{
    if(this->frames != NULL)
    {
        sceneNode.translate(this->frames[0].translation[0], this->frames[0].translation[1], this->frames[0].translation[2]);
        sceneNode.scale(this->frames[0].scale[0], this->frames[0].scale[1], this->frames[0].scale[2]);
    }
}

void MD2Loader::swap(float* array, unsigned char nbElements, unsigned char firstIndex, unsigned char secondIndex)
{
    float tmp;
    for(int i = 0; i < nbElements; i++)
    {
        tmp = array[firstIndex + i];
        array[firstIndex + i] = array[secondIndex + i];
        array[secondIndex + i] = tmp;
    }
}

}
